'''
Created on 2014/5/27

@author: cat
'''
import struct

#Modbus Function Code
Fun_Read_RWR = 0x03     #Read the contents of read/write location
Fun_Read_RR = 0x04      #Read the contents of read only location
Fun_Set_RWR = 0x10      #Set the contents of read/write location 

Type_UInt = 1
Type_Int = 1
Type_Long = 1
Type_Float = 2
Type_Char = 3

Reg_Address = {0x0400 : Type_Float,      #Vln_a 
               0x0402 : Type_Float,      #Vln_b 
               0x0404 : Type_Float,      #Vln_c 
               0x0406 : Type_Float,      #Vln_avg 
               0x0408 : Type_Float,      #Vll_ab 
               0x040a : Type_Float,      #Vll_bc 
               0x040c : Type_Float,      #Vll_ca 
               0x040e : Type_Float,      #Vll_avg 
               0x0410 : Type_Float,      #I_a 
               0x0412 : Type_Float,      #I_b 
               0x0414 : Type_Float,      #I_c 
               0x0416 : Type_Float,      #I_avg 
               0x0418 : Type_Float,      #reserve 
               0x041a : Type_Float,      #Freq 
               0x041c : Type_Float,      #kW_a 
               0x041e : Type_Float,      #kW_b 
               0x0420 : Type_Float,      #kW_c 
               0x0422 : Type_Float,      #kW_total 
               0x0424 : Type_Float,      #kVar_a 
               0x0426 : Type_Float,      #kVar_b 
               0x0428 : Type_Float,      #kVar_c 
               0x042a : Type_Float,      #kVar_total 
               0x042c : Type_Float,      #kVA_a 
               0x042e : Type_Float,      #kVA_b 
               0x0430 : Type_Float,      #kVA_c 
               0x0432 : Type_Float,      #kVA_total 
               0x0434 : Type_Float,      #PF_signed_a 
               0x0436 : Type_Float,      #PF_signed_b 
               0x0438 : Type_Float,      #PF_signed_c 
               0x043a : Type_Float,      #PF_signed_avg 
               0x043c : Type_Float,      #PhaseAngle_V_a 
               0x043e : Type_Float,      #PhaseAngle_V_b 
               0x0440 : Type_Float,      #PhaseAngle_V_c 
               0x0442 : Type_Float,      #PhaseAngle_I_a 
               0x0444 : Type_Float,      #PhaseAngle_I_b 
               0x0446 : Type_Float,      #PhaseAngle_I_c 
               0x0480 : Type_Float,      #kWh_deliver_a 
               0x0482 : Type_Float,      #kWh_receiver_a 
               0x0484 : Type_Float,      #kvarh_lagging_a 
               0x0486 : Type_Float,      #kvarh_leading_a 
               0x0488 : Type_Float,      #kVAh_a 
               0x048a : Type_Float,      #kWh_deliver_b 
               0x048c : Type_Float,      #kWh_receiver_b 
               0x048e : Type_Float,      #kvarh_lagging_b 
               0x0490 : Type_Float,      #kvarh_leading_b 
               0x0492 : Type_Float,      #kVAh_b 
               0x0494 : Type_Float,      #kWh_deliver_c 
               0x0496 : Type_Float,      #kWh_receiver_c 
               0x0498 : Type_Float,      #kvarh_lagging_c 
               0x049a : Type_Float,      #kvarh_leading_c 
               0x049c : Type_Float,      #kVAh_c 
               0x049e : Type_Float,      #kWh_deliver_total 
               0x04a0 : Type_Float,      #kWh_receiver_total 
               0x04a2 : Type_Float,      #kvarh_lagging_total 
               0x04a4 : Type_Float,      #kvarh_leading_total 
               0x04a6 : Type_Float,      #kVAh_total 
               0x11a2 : Type_Float,      #Real time Demand - kW 
               0x11a4 : Type_Float,      #Prev Demand - kW  
               0x1008 : Type_Char        #Meter_ID     
               }

class Modbus(object):
    _buffer = []
    respond_length = 0
    respond_type = 0
    def clean_buffer(self):
        self._buffer.clear()
    
    def get_packet(self, urlquery):
        packet = [15, 3, 16, 0, 0, 1]
        
        packet[0] = list((int(urlquery[0], 16)).to_bytes(1, 'big'))[0]
        packet[1] = list((int(urlquery[1], 16)).to_bytes(1, 'big'))[0]
        
        #if packet[1] == Fun_Set_RWR:
        #    return None
        
        reg = int(urlquery[2], 16)
        if reg in Reg_Address.keys():
            self.respond_type = Reg_Address[reg]
        else:
            self.respond_type = Type_Int
        reg = list((reg).to_bytes(2, 'big'))
        packet[2] = reg[0]
        packet[3] = reg[1]
        
        length = (int(urlquery[3], 16)).to_bytes(2, 'big')
        self.respond_length  = int(urlquery[3], 16) * 2
        length = list(length)
        packet[4] = length[0]
        packet[5] = length[1]
          
        if packet[1] == 0x10:
            data = list((int(urlquery[4], 16)).to_bytes(self.respond_length, 'big'))
            packet = packet + [self.respond_length] + data
        
        self.respond_length += 5
        return packet + self.crc(packet)
    
    def get_msg(self, respond):
        if len(respond) == 0:
            print("length error")
            return ""
        data_length = respond[2]
        
        if len(respond) - 5 != data_length:
            print("msg lost")
        
        data = respond[3 : 3 + data_length]
        if self.respond_type == Type_Char:
            data.reverse()
            return "".join(map(chr, data))
        elif self.respond_type == Type_Float:
            return ("%s" % self.ieee754(data))
        return ("%s" % int.from_bytes(data, byteorder='big'))
            
    def crc(self, msg):
        crc_reg = 0xFFFF
        for data in msg:
            crc_reg ^= data
            for i in range(8):
                if crc_reg & 0x01:
                    crc_reg = (crc_reg >> 1) ^ 0xA001
                else:
                    crc_reg = crc_reg >> 1
                    
        #crc_reg = ((crc_reg << 8) & 0xFF00) | ((crc_reg >> 8) & 0x00FF)
        return list(crc_reg.to_bytes(2, byteorder='little'))
    
    def ieee754(self, msg):    
        data = [msg[2], msg[3], msg[0], msg[1]]
        return struct.unpack('f', data)
        
